#ifndef _NET_H_
#define _NET_H_

#include "common.h"
#include "array.h"
#include "file.h"

#ifdef WIN32
/* the next few lines mean we need winXP or better,
 * better being non-windows, really */
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>
#include <netinet/in.h>
#endif

#define NETSTATE_UNUSED		0
#define NETSTATE_OPEN		1
#define NETSTATE_CLOSED		2

#define NETTYPE_TCP		1
#define NETTYPE_UDP		2
#define NETTYPE_TCP_HOST	3
#define NETTYPE_UDP_HOST	4

#ifndef _HAVE_NETTCP_BUFFER_TYPE
#define _HAVE_NETTCP_BUFFER_TYPE
typedef struct net_tcp_buffer_s {
	unsigned char buff[1024];
	int start;
	int end;
} net_tcp_buffer_t;
#endif

#ifndef _HAVE_NETUDP_BUFFER_TYPE
#define _HAVE_NETUDP_BUFFER_TYPE
typedef struct net_udp_buffer_s {
	file_t *unsorted;
	file_t *sorted;
} net_udp_buffer_t;
#endif

#ifndef _HAVE_NET_TYPE
#define _HAVE_NET_TYPE
typedef struct net_connection_s {
	struct net_connection_s *prev;
	struct net_connection_s *next;
	struct addrinfo hints;
	struct addrinfo *addr;
	struct sockaddr_storage remote_addr;
	socklen_t remote_addr_len;
	int id;
	int type;
	int fd;
	int state;
	int tries;
	char* host;
	char* port;
	union {
		net_tcp_buffer_t tcp;
		net_udp_buffer_t udp;
	} buff;
	array_t *peers;
} net_connection_t;
#endif

/* defined in net.c */
int net_init(void);
void net_exit(void);
net_connection_t *net_connection(void);
net_connection_t *net_fetch(int id);
int net_state(net_connection_t *n);
void net_disconnect(net_connection_t *n);
void net_close(net_connection_t *n);
array_t *net_select(unsigned int msec, int reconnect, net_connection_t *n);
array_t *net_select_array(unsigned int msec, int reconnect, array_t *a);
int net_write(net_connection_t *n, void *buff, unsigned int size);
int net_write_string(net_connection_t *n, char* fmt, ...);
int net_read(net_connection_t *n, void *buff, unsigned int size);
int net_readline(net_connection_t *n, void *buff, unsigned int size);
int net_broadcast(net_connection_t *n, void *buff, unsigned int size);
int net_accept(net_connection_t *n);

/* defined in net_udp.c */
file_t *udp_next(net_connection_t *n);

#ifdef _NET_LOCAL
/* defined in net_tcp.c */
net_connection_t *tcp_client_connect(char* host, char* port);
int tcp_client_reconnect(net_connection_t *n);
net_connection_t *tcp_host_connect(char* host, char* port);
int tcp_host_reconnect(net_connection_t *n);
int tcp_write(net_connection_t *n, void *buff, unsigned int size);
int tcp_pending(net_connection_t *n);
int tcp_read(net_connection_t *n, void *buff, unsigned int size);
int tcp_readline(net_connection_t *n, void *buf, unsigned int size);
int tcp_broadcast(net_connection_t *n, void *buff, unsigned int size);
int tcp_accept(net_connection_t *n);

/* defined in net_udp.c */
net_connection_t *udp_client_connect(char* host, char* port);
int udp_client_reconnect(net_connection_t *n);
net_connection_t *udp_host_connect(char* host, char* port);
int udp_host_reconnect(net_connection_t *n);
int udp_write(net_connection_t *n, void *buff, unsigned int size);
int udp_pending(net_connection_t *n);
int udp_read(net_connection_t *n, void *buff, unsigned int size);
int udp_readline(net_connection_t *n, void *buf, unsigned int size);
int udp_broadcast(net_connection_t *n, void *buff, unsigned int size);
int udp_accept(net_connection_t *n);
#endif

#endif
